﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
namespace RapidRepository
{
    /// <summary>
    /// Manages files and directories within the document database.
    /// </summary>
    internal class FileManager : IFileManager
    {
        #region Declarations
        
        public const string rapidRepositoryDirectory = "RapidRepository";
        const string viewDirectory = "Views";
        const string jsonExtension = ".json";

        #endregion

        #region Save

        /// <summary>
        /// Saves the specified type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="id">The id.</param>
        /// <param name="serialisedObject">The serialised object.</param>
        public void Save(Type type, Guid id, string serialisedObject)
        {
            Contract.Requires("type", type != null);
            Contract.Requires("id", id != Guid.Empty);
            Contract.Requires("serialisedObject", !string.IsNullOrEmpty(serialisedObject));

            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    CreateBaseDirectoryIfNotExists(store);

                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(type));

                    CreateDirectoryIfNotExists(store, typeDirectoryPath);

                    string filePath = Path.Combine(typeDirectoryPath, id.ToString() + jsonExtension);

                    using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(filePath, FileMode.Create, store))
                    {
                        using (StreamWriter stream = new StreamWriter(fileStream))
                        {
                            stream.Write(serialisedObject);
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        } 

        #endregion

        #region Delete

        /// <summary>
        /// Deletes the specified type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="id">The id.</param>
        public void Delete(Type type, Guid id)
        {
            Contract.Requires("type", type != null);
            Contract.Requires("id", id != Guid.Empty);

            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    AssertRapidDatabaseExists(store);

                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(type));

                    AssertTypeDirectoryExists(type, store, typeDirectoryPath);

                    string filePath = Path.Combine(typeDirectoryPath, id.ToString() + jsonExtension);

                    store.DeleteFile(filePath);
                }
            }
            catch
            {
                throw;
            }
        } 

        #endregion

        #region Exists

        /// <summary>
        /// Returns true if the entity exists on the file system.
        /// </summary>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <param name="id">The id.</param>
        public bool Exists<T>(Guid id)
        {
            Contract.Requires("file id", id != Guid.Empty);

            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(typeof(T)));

                    if (!store.DirectoryExists(typeDirectoryPath))
                    {
                        return false;
                    }

                    string fileName = CreateFileName(id);

                    foreach(string storedFileName in store.GetFileNames(typeDirectoryPath + "/" + Path.GetFileName("*")))
                    {
                        if (storedFileName == fileName)
                        {
                            return true;
                        }
                    }
                    return false;                
                }
            }
            catch
            {
                throw;
            }

        }

        #endregion

        #region GetFile

        /// <summary>
        /// Gets the file by id.
        /// </summary>
        /// <typeparam name="T">The type of the object.</typeparam>
        /// <param name="id">The id.</param>
        public string GetFile<T>(Guid id)
        {
            Contract.Requires("file id", id != Guid.Empty);

            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(typeof(T)));

                    if (!store.DirectoryExists(typeDirectoryPath))
                    {
                        return string.Empty;
                    }

                    return GetFileContent(store, typeDirectoryPath, CreateFileName(id));
                }
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region LoadFiles

        /// <summary>
        /// Loads the files.
        /// </summary>
        /// <typeparam name="T">The type of the objects.</typeparam>
        public string[] LoadFiles<T>()
        {
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(typeof(T)));

                    if (!store.DirectoryExists(typeDirectoryPath))
                    {
                        return new string[] { };
                    }
                    
                    List<string> fileContents = new List<string>();

                    string[] fileNames = store.GetFileNames(typeDirectoryPath + "/" + Path.GetFileName("*"));

                    foreach (string fileName in fileNames)
                    {
                        AddFileContent(store, typeDirectoryPath, fileContents, fileName);
                    }

                    return fileContents.ToArray();
                }
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region SaveViewFile

        /// <summary>
        /// Saves the view file.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="serialisedObject">The serialised object.</param>
        public void SaveViewFile(Type type, string serialisedObject)
        {
            try
            {
                using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    CreateBaseDirectoryIfNotExists(store);

                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(type));

                    CreateDirectoryIfNotExists(store, typeDirectoryPath);

                    string viewDirectoryPath = Path.Combine(typeDirectoryPath, viewDirectory);

                    CreateDirectoryIfNotExists(store, viewDirectoryPath);

                    string filePath = Path.Combine(viewDirectoryPath, type.FullName + jsonExtension);

                    using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(filePath, FileMode.Create, store))
                    {
                        using (StreamWriter stream = new StreamWriter(fileStream))
                        {
                            stream.Write(serialisedObject);
                        }
                    }
                }
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region GetViewFile

        /// <summary>
        /// Gets the view file.
        /// </summary>
        /// <param name="viewType">Type of the view.</param>
        public string GetViewFile(Type viewType)
        {
            try
            {
                using (var store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!store.DirectoryExists(rapidRepositoryDirectory))
                    {
                        return null;
                    }

                    string typeDirectoryPath = Path.Combine(rapidRepositoryDirectory, GetDirectoryPath(viewType));

                    if (!store.DirectoryExists(typeDirectoryPath))
                    {
                        return null;
                    }
                    
                    string viewDirectoryPath = Path.Combine(typeDirectoryPath, viewDirectory);

                    if (!store.DirectoryExists(viewDirectoryPath))
                    {
                        return null;
                    }

                    string fileName = viewType.FullName + jsonExtension;

                    if(!store.FileExists(Path.Combine(viewDirectoryPath, fileName)))
                    {
                        return null;
                    }

                    return GetFileContent(store, viewDirectoryPath, fileName);
                }
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region Private Methods
        
        private static void AddFileContent(IsolatedStorageFile store, string typeDirectoryPath, List<string> fileContents, string fileName)
        {
            fileContents.Add(GetFileContent(store, typeDirectoryPath, fileName));
        }

        private static string GetFileContent(IsolatedStorageFile store, string typeDirectoryPath, string fileName)
        {
            string filePath = Path.Combine(typeDirectoryPath, fileName);

            using (IsolatedStorageFileStream fileStream = new IsolatedStorageFileStream(filePath, FileMode.Open, store))
            {
                using (TextReader reader = new StreamReader(fileStream))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        private static string GetDirectoryPath(Type type)
        {
            return type.FullName;
        }

        private static string CreateFileName(Guid entityId)
        {
            return string.Format("{0}.json", entityId.ToString());
        }

        private static void AssertRapidDatabaseExists(IsolatedStorageFile store)
        {
            if (!store.DirectoryExists(rapidRepositoryDirectory))
            {
                throw new InvalidOperationException("The RapidRepository directory does not exists");
            }
        }

        private static void AssertTypeDirectoryExists(Type type, IsolatedStorageFile store, string typeDirectory)
        {
            if (!store.DirectoryExists(typeDirectory))
            {
                throw new InvalidOperationException(string.Format("The {0} directory does not exist.", type.FullName));
            }
        }

        private static void CreateBaseDirectoryIfNotExists(IsolatedStorageFile store)
        {
            CreateDirectoryIfNotExists(store, rapidRepositoryDirectory);
        }

        private static void CreateDirectoryIfNotExists(IsolatedStorageFile store, string directory)
        {
            if (!store.DirectoryExists(directory))
            {
                store.CreateDirectory(directory);
            }
        }

        #endregion
    }
}
